package com.uinnova.product.eam.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.binary.core.util.BinaryUtils;
import com.binary.jdbc.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.comm.diagram.CheckType;
import com.uinnova.product.eam.comm.model.es.EamCategory;
import com.uinnova.product.eam.config.Env;
import com.uinnova.product.eam.model.*;
import com.uinnova.product.eam.model.bm.SavePrivateBatchCIContext;
import com.uinnova.product.eam.model.diagram.ArtifactCheckDto;
import com.uinnova.product.eam.model.diagram.DiagramNodeJson;
import com.uinnova.product.eam.model.diagram.DiagramNodeLinkInfo;
import com.uinnova.product.eam.model.dm.DataModelAttribute;
import com.uinnova.product.eam.model.dm.DataModelBatchResp;
import com.uinnova.product.eam.model.dm.DataModelCiInfo;
import com.uinnova.product.eam.model.dm.DataModelDiagramParam;
import com.uinnova.product.eam.model.dm.bean.DataModelEntityNodeVo;
import com.uinnova.product.eam.model.enums.ArtifactType;
import com.uinnova.product.eam.service.*;
import com.uinnova.product.eam.service.dm.DataModelSvc;
import com.uinnova.product.eam.service.utils.DataModelDiagramUtil;
import com.uinnova.product.eam.service.utils.VisualModelUtils;
import com.uinnova.product.vmdb.comm.model.ci.CCcCiClass;
import com.uinnova.product.vmdb.comm.model.ci.CcCi;
import com.uinnova.product.vmdb.comm.model.ci.CcCiAttrDef;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiClassInfo;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiInfo;
import com.uinnova.product.vmdb.provider.rlt.bean.CcCiRltInfo;
import com.uinnova.project.api.diagram.v2.client.ESDiagramApiClient;
import com.uinnova.project.base.diagram.comm.model.ESDiagram;
import com.uinnova.project.base.diagram.comm.model.ESDiagramLink;
import com.uinnova.project.base.diagram.comm.model.ESDiagramModel;
import com.uinnova.project.base.diagram.comm.model.ESDiagramNode;
import com.uino.api.client.cmdb.IRltClassApiSvc;
import com.uino.bean.cmdb.base.*;
import com.uino.bean.cmdb.business.BindCiRltRequestDto;
import com.uino.bean.cmdb.query.ESRltSearchBean;
import com.uino.dao.cmdb.ESCIClassSvc;
import com.uino.dao.cmdb.ESCIRltSvc;
import com.uino.dao.cmdb.ESVisualModelSvc;
import com.uino.dao.util.ESUtil;
import com.uino.util.sys.SysUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 莫奈画布操作接口实现类
 * @author ch
 * @date 2021-10-28
 */
@Slf4j
@Service
public class CanvasDiagramSvcImpl implements ICanvasDiagramSvc {

    @Autowired
    private ESCIClassSvc ciClassSvc;
    @Autowired
    private IRltClassApiSvc rltClassSvc;
    @Autowired
    private ESCIRltSvc esCiRltSvc;
    @Autowired
    private ICISwitchSvc iciSwitchSvc;
    @Resource
    private IamsCIPrivateSvc ciPrivateSvc;
    @Autowired
    private ICIRltSwitchSvc iciRltSwitchSvc;
    @Resource
    private ESDiagramApiClient diagramApiClient;
    @Resource
    private EamCategorySvc categorySvc;
    @Autowired
    private ESVisualModelSvc esVisualModelSvc;
    @Resource
    private IEamArtifactSvc artifactSvc;
    @Autowired
    private DataModelSvc dataModelSvc;
    @Resource
    private IEamCIClassApiSvc ciClassApiSvc;
    @Resource
    private IEamArtifactColumnSvc artifactColumnSvc;

    @Override
    public List<CcCiClassInfo> queryCiRltInfo(Long from, Long to) {
        Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
        // 填补分类下关系数量信息
        Map<Long, CcCiClassInfo> rltClssMap = new HashMap<>();
        List<CcCiClassInfo> rltClss = rltClassSvc.queryAllClasses(domainId);

        Set<Long> clsIds = rltClss.stream().map(ccCiClassInfo -> ccCiClassInfo.getCiClass().getId()).collect(Collectors.toSet());
        Map<String, Long> countRes = esCiRltSvc.groupByCountField("classId", QueryBuilders.termsQuery("classId", clsIds));
        rltClss.forEach(cls -> {
            Long count = countRes.get(cls.getCiClass().getId().toString());
            count = count == null ? 0L : count;
            cls.setCiCount(count);
        });
        for (CcCiClassInfo classInfo : rltClss) {
            rltClssMap.put(classInfo.getCiClass().getId(), classInfo);
        }

        // 获取模型
        Map<Long, CcCiClassInfo> resultMap = new HashMap<>();
        ESVisualModel esVisualModel = esVisualModelSvc.getEnableModel(domainId);
        if(esVisualModel == null){
            return Collections.emptyList();
        }
        List<DiagramNodeLinkInfo> rltLinkList = VisualModelUtils.getRltClassIds(esVisualModel);
        for (DiagramNodeLinkInfo link : rltLinkList) {
            if(link.getSourceId().equals(from) && link.getTargetId().equals(to)){
                resultMap.put(link.getLinkId(), rltClssMap.get(link.getLinkId()));
            }
        }
        if(resultMap.isEmpty()) {
            resultMap = rltClssMap;
        }

        return new ArrayList<>(resultMap.values());
    }

    @Override
    public List<EamCiUpdateDto> checkCiInfoByDiagramId(String energyId, Boolean needDetail) {
        List<EamCiUpdateDto> result = new ArrayList<>();
        ESDiagram diagramInfo = diagramApiClient.getEsDiagram(energyId, 0);
        if(diagramInfo == null){
            return result;
        }
        String ownerCode = diagramInfo.getOwnerCode();
        //取出视图中所有Ci及Rlt
        List<ESDiagramModel> modelList = new ArrayList<>();
        //排除继承节点
        EamCategory category = categorySvc.selectByDiagramId(energyId, ownerCode, LibType.PRIVATE);
        List<Long> diagramId = Collections.singletonList(diagramInfo.getId());
        List<ESDiagramNode> nodes = diagramApiClient.selectNodeByDiagramIds(diagramId);
        if(BinaryUtils.isEmpty(nodes)){
            return result;
        }
        List<ESDiagramLink> links = diagramApiClient.selectLinkByDiagramIds(diagramId);
        Map<String, List<ESDiagramLink>> linkGroup = new HashMap<>();
        if(!BinaryUtils.isEmpty(links)){
            linkGroup = links.stream().collect(Collectors.groupingBy(ESDiagramLink::getSheetId));
        }
        Map<String, List<ESDiagramNode>> nodeGroup = nodes.stream().collect(Collectors.groupingBy(ESDiagramNode::getSheetId));
        CcCiClassInfo rltClass = rltClassSvc.getRltClassByName(SysUtil.getCurrentUserInfo().getDomainId(), Env.DM_INCLUDE);
        CcCiClassInfo attrClass = ciClassApiSvc.getCIClassByCodes(Env.ATTRIBUTES.getCode());
        for (String sheetId : nodeGroup.keySet()) {
            List<ESDiagramNode> nodeList = nodeGroup.getOrDefault(sheetId, Collections.emptyList());
            if(BinaryUtils.isEmpty(nodeList)){
                continue;
            }
            EamCiUpdateDto updateDto = new EamCiUpdateDto();
            updateDto.setDiagramId(energyId);
            updateDto.setOwnerCode(ownerCode);
            updateDto.setSheetId(sheetId);
            List<EamElementUpdateInfo> ciCheckResult = checkCiVersion(diagramInfo.getDomainId(), nodeList, rltClass, attrClass, category, ownerCode);
            List<ESDiagramLink> linkList = linkGroup.getOrDefault(sheetId, Collections.emptyList());
            Set<String> rltCodeList = linkList.stream().map(ESDiagramLink::getUniqueCode).filter(Objects::nonNull).collect(Collectors.toSet());
            List<EamElementUpdateInfo> rltCheckResult = checkRltVersion(rltCodeList, ownerCode);
            if(!needDetail){
                ciCheckResult.forEach(info -> info.setCiInfo(null));
                rltCheckResult.forEach(info -> info.setRltInfo(null));
            }
            updateDto.setCiList(ciCheckResult);
            updateDto.setRltList(rltCheckResult);
            result.add(updateDto);
        }
        return result;
    }

    private List<CcCiInfo> checkEntityAttr(String entityCode, String ownerCode, CcCiClassInfo rltClass, CcCiClassInfo attrClass, List<String> attrNames){
        //根据设计库关系查询实体属性
        List<CcCiRltInfo> rltList = dataModelSvc.getAttrByEntity(rltClass.getCiClass().getId(), attrClass.getCiClass().getId(), Sets.newHashSet(entityCode), ownerCode, LibType.DESIGN);
        if(BinaryUtils.isEmpty(rltList)){
            return Collections.emptyList();
        }
        List<CcCiInfo> result = new ArrayList<>();
        for (CcCiRltInfo each : rltList) {
            CcCiInfo ciInfo = each.getTargetCiInfo();
            String name = ciInfo.getAttrs().get(DataModelAttribute.NAME_CN);
            if(BinaryUtils.isEmpty(name) || attrNames.contains(name)){
                continue;
            }
            result.add(ciInfo);
        }
        return result;
    }

    /**
     * 校验是否是ER图
     */
    private boolean checkDataModelDiagram(String artifactId){
        if(BinaryUtils.isEmpty(artifactId)){
            return false;
        }
        List<EamArtifactVo> artifactLit = artifactSvc.queryByType(Lists.newArrayList(6, 7, 11, 12));
        List<Long> entityArtifact = artifactLit.stream().map(EamArtifactVo::getId).collect(Collectors.toList());
        return entityArtifact.contains(Long.parseLong(artifactId));
    }

    private List<Long> getEntityClassId(){
        CCcCiClass cdt = new CCcCiClass();
        cdt.setClassCodes(new String[]{Env.LOGIC_ENTITY.getCode(), Env.CONCEPTION_ENTITY.getCode(), Env.PHYSICAL_ENTITY.getCode()});
        List<CcCiClassInfo> classInfoList = ciClassSvc.queryCiClassInfoList(cdt, null, false);
        if(BinaryUtils.isEmpty(classInfoList)){
            return Collections.emptyList();
        }
        return classInfoList.stream().map(each -> each.getCiClass().getId()).collect(Collectors.toList());
    }

    @Override
    public DataModelBatchResp updateCiInfoByDiagramId(String energyId) {
        ESDiagram diagramInfo = diagramApiClient.getEsDiagram(energyId, 0);
        String ownerCode = diagramInfo.getOwnerCode();
        //排除继承节点
        List<Long> diagramId = Collections.singletonList(diagramInfo.getId());
        List<ESDiagramNode> nodeList = diagramApiClient.selectNodeByDiagramIds(diagramId);
//        EamDiagramCatalog catalog = catalogSvc.selectByDiagramId(energyId, ownerCode, LibType.PRIVATE);
        EamCategory category = categorySvc.selectByDiagramId(energyId, ownerCode, LibType.PRIVATE);
        CcCiClassInfo rltClass = rltClassSvc.getRltClassByName(SysUtil.getCurrentUserInfo().getDomainId(), Env.DM_INCLUDE);
        CcCiClassInfo attrClass = ciClassApiSvc.getCIClassByCodes(Env.ATTRIBUTES.getCode());
        DataModelBatchResp result = new DataModelBatchResp();
        result.setAttributeDefs(attrClass.getAttrDefs());
        result.setAttributeCiClass(attrClass.getCiClass());
        List<EamElementUpdateInfo> ciUpdateInfo = checkCiVersion(diagramInfo.getDomainId(), nodeList, rltClass, attrClass, category, ownerCode);
        //取出所有要更新的ci
        List<ESCIInfo> ciUpdateList = new ArrayList<>();
        Set<Long> classIds = new HashSet<>();
        DataModelDiagramParam diagramParam = dataModelSvc.getDiagramParam(energyId, null);
        Map<String, List<CcCiInfo>> entityAttrMap = new HashMap<>();
        Map<String, ESCIInfo> entityMap = new HashMap<>();
        for (EamElementUpdateInfo each : ciUpdateInfo) {
            if(CheckType.UPDATE.equals(each.getState())){
                ciUpdateList.add(each.getCiInfo());
                classIds.add(each.getCiInfo().getClassId());
            }
            if(BinaryUtils.isEmpty(each.getAttrUpdateInfo())){
                continue;
            }
            List<String> attrNames = new ArrayList<>();
            for (EamElementUpdateInfo attr : each.getAttrUpdateInfo()) {
                if(CheckType.UPDATE.equals(attr.getState())){
                    ciUpdateList.add(attr.getCiInfo());
                    classIds.add(attr.getCiInfo().getClassId());
                }
                Object name = attr.getCiInfo().getAttrs().get(DataModelAttribute.NAME_CN);
                if(BinaryUtils.isEmpty(name)){
                    continue;
                }
                attrNames.add(name.toString());
            }
            if(BinaryUtils.isEmpty(each.getDesignCode())){
                continue;
            }
            List<CcCiInfo> addAttr = checkEntityAttr(each.getDesignCode(), ownerCode, rltClass, attrClass, attrNames);
            entityAttrMap.put(each.getKey(), addAttr);
            entityMap.put(each.getKey(), each.getCiInfo());
        }
        List<Long> makeZeroCiIds = new ArrayList<>();
        if(!BinaryUtils.isEmpty(ciUpdateList)){
            Map<String, SavePrivateBatchCIContext> contextMap = ciPrivateSvc.saveOrUpdateBatchCI(ciUpdateList, new ArrayList<>(classIds), ownerCode, ownerCode);
            List<Long> ids = contextMap.values().stream().map(SaveBatchCIContext::getId).collect(Collectors.toList());
            makeZeroCiIds.addAll(ids);
        }
        if(BinaryUtils.isEmpty(entityAttrMap.keySet()) || BinaryUtils.isEmpty(attrClass) || BinaryUtils.isEmpty(rltClass)){
            ciPrivateSvc.makeZeroCiLocalVersionByIds(makeZeroCiIds);
            return result;
        }
        Long rltClassId = rltClass.getCiClass().getId();
        Long attrClassId = attrClass.getCiClass().getId();
        List<CcCiAttrDef> attrDef = EamUtil.copy(attrClass.getAttrDefs(), CcCiAttrDef.class);
        Map<String, List<CcCiInfo>> resultMap = new HashMap<>();
        for (String key : entityAttrMap.keySet()) {
            List<CcCiInfo> attrList = entityAttrMap.get(key);
            if(BinaryUtils.isEmpty(attrList)){
                continue;
            }
            for (CcCiInfo eachAttr : attrList) {
                eachAttr.getCi().setId(null);
                eachAttr.getCi().setClassId(attrClassId);
                eachAttr.getCi().setOwnerCode(ownerCode);
                eachAttr.getAttrs().put(DataModelAttribute.INHERIT_ID, Long.toString(ESUtil.getUUID()));
                eachAttr.getAttrs().put(DataModelAttribute.ENTITY, key);
            }
            List<ESCIInfo> coverCiList = EamUtil.coverCiInfoList(attrList);
            Map<String, SavePrivateBatchCIContext> contextMap = ciPrivateSvc.saveOrUpdateBatchCI(coverCiList, Lists.newArrayList(attrClassId), ownerCode, ownerCode);
            List<Long> ids = contextMap.values().stream().map(SaveBatchCIContext::getId).filter(Objects::nonNull).collect(Collectors.toList());
            makeZeroCiIds.addAll(ids);
            List<DataModelCiInfo> inheritCiList = new ArrayList<>();
            ESCIInfo entity = entityMap.get(key);
            List<BindCiRltRequestDto> bindList = new ArrayList<>();
            List<DataModelCiInfo> primaryCiList = new ArrayList<>();
            for (String attrCode : contextMap.keySet()) {
                CcCiInfo attr = EamUtil.coverESCIInfo(contextMap.get(attrCode).getEsCi(), null, null);
                DataModelCiInfo inheritCi = EamUtil.copy(attr, DataModelCiInfo.class);
                boolean primaryKey = Boolean.parseBoolean(attr.getAttrs().get(DataModelAttribute.PRIMARY_KEY));
                if(primaryKey){
                    inheritCiList.add(inheritCi);
                }
                BindCiRltRequestDto bindRlt = BindCiRltRequestDto.builder().ownerCode(ownerCode).repetitionError(false).rltClassId(rltClassId)
                        .custom1("3").sourceCiId(entity.getId()).targetCiId(attr.getCi().getId()).build();
                bindList.add(bindRlt);
            }
            iciRltSwitchSvc.bindCiRltBatch(bindList, LibType.PRIVATE);
            Map<String, List<CcCiInfo>> inheritMap = new HashMap<>();
            if(CollectionUtils.isNotEmpty(inheritCiList)){
                inheritMap = dataModelSvc.inheritAttribute(key, diagramParam, inheritCiList);
            }
            inheritMap.put(key, attrList);
            dataModelSvc.updateInheritDiagramNode(diagramParam.getModel(), inheritMap);
            resultMap.putAll(inheritMap);
        }
        //刷新视图中实体属性字段
        refreshDiagramAttrNode(diagramId, ownerCode);
        result.setInheritMap(resultMap);
        ciPrivateSvc.makeZeroCiLocalVersionByIds(makeZeroCiIds);
        return result;
    }

    @Override
    public List<ArtifactCheckDto> checkByArtifact(String diagramId) {
        ESDiagram diagram = diagramApiClient.getEsDiagram(diagramId, 0);
        List<ArtifactCheckDto> result = new ArrayList<>();
        if(BinaryUtils.isEmpty(diagram) || BinaryUtils.isEmpty(diagram.getViewType())){
            return result;
        }
        List<ESDiagramLink> diagramLinks = diagramApiClient.selectLinkByDiagramIds(Collections.singletonList(diagram.getId()));
        List<ESDiagramNode> diagramNodes = diagramApiClient.selectNodeByDiagramIds(Collections.singletonList(diagram.getId()));
        if(CollectionUtils.isEmpty(diagramLinks) || CollectionUtils.isEmpty(diagramNodes)){
            return result;
        }
        Map<String, String> nodeKeyMap = new HashMap<>();
        Map<String, String> nodeNameMap = new HashMap<>();
        for (ESDiagramNode node : diagramNodes) {
            JSONObject nodeObj = JSON.parseObject(node.getNodeJson());
            nodeKeyMap.put(nodeObj.getString("key"), nodeObj.getString("classCode"));
            nodeNameMap.put(nodeObj.getString("key"), nodeObj.getString("label"));
        }
        List<Integer> type = Collections.singletonList(ArtifactType.RLT_TYPE.val());
        List<EamArtifactElementVo> rltElements = artifactColumnSvc.queryByArtifactId(Long.parseLong(diagram.getViewType()), type);
        if(BinaryUtils.isEmpty(diagramLinks)){
            return result;
        }
        List<EamArtifactRltVo> objList = rltElements.stream().map(EamArtifactElementVo::getElementRltObj).flatMap(Collection::stream).filter(EamArtifactRltVo::getViewFlag).collect(Collectors.toList());
        Set<String> rltClass = objList.stream().map(e->e.getSourceCiInfo().getClassCode()+"-"+e.getRltClassInfo().getCiClass().getId()+"-"+e.getTargetCiInfo().getClassCode()).collect(Collectors.toSet());
        for (ESDiagramLink link : diagramLinks) {
            if(BinaryUtils.isEmpty(link.getUniqueCode())){
                continue;
            }
            JSONObject linkObj = JSON.parseObject(link.getLinkJson());
            String from = linkObj.getString("from");
            String to = linkObj.getString("to");
            String classCode = linkObj.getString("classId");
            if(rltClass.contains(nodeKeyMap.get(from) + "-" + classCode + "-" + nodeKeyMap.get(to))){
                continue;
            }
            ArtifactCheckDto dto = new ArtifactCheckDto();
            dto.setKey(linkObj.getString("key"));
            dto.setName(linkObj.getString("label"));
            dto.setUniqueCode(link.getUniqueCode());
            dto.setSourceName(nodeNameMap.get(from));
            dto.setTargetName(nodeNameMap.get(to));
            result.add(dto);
        }
        return result;
    }

    private void refreshDiagramAttrNode(List<Long> diagramId, String ownerCode) {
        List<ESDiagramNode> nodeList = diagramApiClient.selectNodeByDiagramIds(diagramId);
        if(BinaryUtils.isEmpty(nodeList)){
            return;
        }
        Set<String> ciCodes = DataModelDiagramUtil.getNodeCiCode(nodeList);
        if(BinaryUtils.isEmpty(ciCodes)){
            return;
        }
        List<ESCIInfo> ciInfoList = iciSwitchSvc.getCiByCodes(new ArrayList<>(ciCodes), ownerCode, LibType.PRIVATE);
        if(BinaryUtils.isEmpty(ciInfoList)){
            return;
        }
        Map<String, ESCIInfo> ciMap = ciInfoList.stream().collect(Collectors.toMap(CcCi::getCiCode, each -> each, (k1, k2) -> k2));
        for (ESDiagramNode node : nodeList) {
            if(BinaryUtils.isEmpty(node.getCiCode()) || BinaryUtils.isEmpty(node.getNodeJson())){
                continue;
            }
            //添加处理ER图实体属性节点情况
            DiagramNodeJson nodeJson = JSON.parseObject(node.getNodeJson(), DiagramNodeJson.class);
            if(BinaryUtils.isEmpty(nodeJson.getItems())){
                continue;
            }
            for (DataModelEntityNodeVo item : nodeJson.getItems()) {
                ESCIInfo attr = ciMap.get(item.getCiCode());
                if(BinaryUtils.isEmpty(attr)){
                    continue;
                }
                CcCiInfo ciInfo = EamUtil.coverESCIInfo(attr, null, null);
                item.setAttrs(ciInfo.getAttrs());
            }
            node.setNodeJson(JSON.toJSONString(nodeJson));
        }
        diagramApiClient.saveNodeList(nodeList);
    }

    /**
     * 私有库关系与运行库关系对比
     */
    private List<EamElementUpdateInfo> checkRltVersion(Set<String> rltCodes, String ownerCode){
        if(BinaryUtils.isEmpty(rltCodes)){
            return Collections.emptyList();
        }
        List<EamElementUpdateInfo> checkResult = new ArrayList<>();

        //根据rltCode查询私有库
        ESRltSearchBean rltPrivateQuery = new ESRltSearchBean();
        rltPrivateQuery.setRltCodes(rltCodes);
        rltPrivateQuery.setOwnerCode(ownerCode);
        Page<ESCIRltInfo> rltPrivateInfoPage = iciRltSwitchSvc.searchRlt(rltPrivateQuery, LibType.PRIVATE);
        List<ESCIRltInfo> privateRltInfos = rltPrivateInfoPage.getData();
        //私有库和设计库对比CI是否需要更新(对比private中publicVersion和design中publicVersion)
        for(ESCIRltInfo privateRlt : privateRltInfos){
            EamElementUpdateInfo ele = new EamElementUpdateInfo(privateRlt.getCiCode(), CheckType.NONE);
            ele.setRltInfo(privateRlt);
            checkResult.add(ele);
        }
        return checkResult;
    }

    /**
     * 私有库对象与运行库对象对比
     */
    private List<EamElementUpdateInfo> checkCiVersion(Long domainId, List<ESDiagramNode> nodeList, CcCiClassInfo rltClass, CcCiClassInfo attrClass, EamCategory category, String ownerCode){
        Set<String> ciCodes = DataModelDiagramUtil.getNodeCiCode(nodeList);
        if(!BinaryUtils.isEmpty(category)){
            ciCodes.remove(category.getCiCode());
        }
        if(BinaryUtils.isEmpty(ciCodes)){
            return Collections.emptyList();
        }
        List<Long> entityClassId = getEntityClassId();
        List<EamElementUpdateInfo> checkResult = new ArrayList<>();
        //根据ciCode查询私有库
        List<ESCIInfo> privateCiList = iciSwitchSvc.getCiByCodes(new ArrayList<>(ciCodes), ownerCode, LibType.PRIVATE);
        if(BinaryUtils.isEmpty(privateCiList)){
            return Collections.emptyList();
        }
        //根据业务主键查询设计库
        Map<String, ESCIInfo> privateMap = new HashMap<>(privateCiList.size());
        for (ESCIInfo each : privateCiList) {
            privateMap.put(each.getCiCode(), each);
        }
        List<String> ciCodeList = new ArrayList<>(privateMap.keySet());
        List<ESCIInfo> designCiList = iciSwitchSvc.getCiByCodes(ciCodeList, ownerCode, LibType.DESIGN);
        Map<String, ESCIInfo> designMap = designCiList.stream().collect(Collectors.toMap(CcCi::getCiCode, each -> each, (k1, k2) -> k2));
        //获取历史设计库ci版本
        Map<String, Long> designMaxVersion = iciSwitchSvc.getCICodeMaxVersion(ciCodeList, LibType.DESIGN);
        //私有库和设计库对比CI是否需要更新(对比private中publicVersion和design中version)
        for (ESDiagramNode node : nodeList) {
            if(BinaryUtils.isEmpty(node.getCiCode())){
                continue;
            }
            ESCIInfo ciInfo = privateMap.get(node.getCiCode());
            if(BinaryUtils.isEmpty(ciInfo)){
                continue;
            }
            EamElementUpdateInfo ele = new EamElementUpdateInfo(node.getCiCode(), CheckType.NONE);
            ESCIInfo designCi = designMap.get(ciInfo.getCiCode());
            if(!BinaryUtils.isEmpty(designCi)){
                ele.setDesignCode(designCi.getCiCode());
                if(BinaryUtils.isEmpty(designCi.getPublicVersion())){
                    continue;
                }
                if(BinaryUtils.isEmpty(ciInfo.getPublicVersion()) || designCi.getPublicVersion()>ciInfo.getPublicVersion()){
                    ele.setState(CheckType.UPDATE);
                    ciInfo.setAttrs(designCi.getAttrs());
                    ciInfo.setPublicVersion(designCi.getPublicVersion());
                    ciInfo.setModifier(designCi.getModifier());
                    ciInfo.setModifyTime(designCi.getModifyTime());
                }
            } else {
                //设计库中没有，但历史库有，则说明设计库已删除，则提示是否删除
                if(!BinaryUtils.isEmpty(designMaxVersion.get(ciInfo.getCiCode()))){
                    ele.setState(CheckType.DELETE);
                }
            }
            ele.setCiInfo(ciInfo);
            //对实体做特殊处理
            if(designCi!=null && entityClassId.contains(ciInfo.getClassId()) && rltClass!=null && attrClass!=null){
                checkAttrVersion(ele, node, privateMap, designMap, designMaxVersion, designCi, rltClass, attrClass);
            }
            checkResult.add(ele);
        }
        return checkResult;
    }

    private List<String> checkAttrVersion(EamElementUpdateInfo ele, ESDiagramNode node,Map<String, ESCIInfo> privateMap, Map<String, ESCIInfo> designMap,
                                          Map<String, Long> designMaxVersion, ESCIInfo designCi, CcCiClassInfo rltClass, CcCiClassInfo attrClass){
        List<String> attrNames = new ArrayList<>();
        if(BinaryUtils.isEmpty(node.getNodeJson())){
            return attrNames;
        }
        //添加处理ER图实体属性节点情况
        DiagramNodeJson nodeJson = JSON.parseObject(node.getNodeJson(), DiagramNodeJson.class);
        if(BinaryUtils.isEmpty(nodeJson.getItems())){
            return attrNames;
        }
        List<String> attrCodes = nodeJson.getItems().stream().map(DataModelEntityNodeVo::getCiCode).filter(Objects::nonNull).collect(Collectors.toList());
        if(BinaryUtils.isEmpty(attrCodes)){
            return attrNames;
        }
        //根据设计库关系查询实体属性
        Long rltClassId = rltClass.getCiClass().getId();
        Long attrClassId = attrClass.getCiClass().getId();
        List<CcCiRltInfo> designRltList = dataModelSvc.getAttrByEntity(rltClassId, attrClassId, Sets.newHashSet(designCi.getCiCode()), ele.getCiInfo().getOwnerCode(), LibType.DESIGN);
        Map<String, ESCIInfo> designNameMap = new HashMap<>();
        for (CcCiRltInfo each : designRltList) {
            CcCiInfo targetCiInfo = each.getTargetCiInfo();
            ESCIInfo ciInfo = EamUtil.coverCiInfo(targetCiInfo);
            String name = targetCiInfo.getAttrs().get(DataModelAttribute.NAME_CN);
            designNameMap.put(name, ciInfo);
        }
        boolean flag = false;
        for (String attrCode : attrCodes) {
            ESCIInfo attrCiInfo = privateMap.get(attrCode);
            if(BinaryUtils.isEmpty(attrCiInfo)){
                continue;
            }
            EamElementUpdateInfo attrEle = new EamElementUpdateInfo(attrCode, CheckType.NONE);
            ESCIInfo attrDesignCi = designMap.get(attrCiInfo.getCiCode());
            Object name = attrCiInfo.getAttrs().get(DataModelAttribute.NAME_CN);
            if(!BinaryUtils.isEmpty(name)){
                attrNames.add(name.toString());
            }
            if(BinaryUtils.isEmpty(attrDesignCi) && !BinaryUtils.isEmpty(name)){
                attrDesignCi = designNameMap.remove(name.toString());
            }else{
                Object designName = attrDesignCi.getAttrs().get(DataModelAttribute.NAME_CN);
                designNameMap.remove(designName.toString());
            }
            if(!BinaryUtils.isEmpty(attrDesignCi)){
                attrEle.setDesignCode(attrDesignCi.getCiCode());
                if(BinaryUtils.isEmpty(attrDesignCi.getPublicVersion())){
                    continue;
                }
                if(BinaryUtils.isEmpty(attrCiInfo.getPublicVersion()) || attrDesignCi.getPublicVersion()>attrCiInfo.getPublicVersion()){
                    flag = true;
                    attrEle.setState(CheckType.UPDATE);
                    attrCiInfo.setAttrs(attrDesignCi.getAttrs());
                    attrCiInfo.setPublicVersion(attrDesignCi.getPublicVersion());
                    attrCiInfo.setModifier(attrDesignCi.getModifier());
                    attrCiInfo.setModifyTime(attrDesignCi.getModifyTime());
                }
            } else {
                //设计库中没有，但历史库有，则说明设计库已删除，则提示是否删除
                if(!BinaryUtils.isEmpty(designMaxVersion.get(attrCiInfo.getCiCode()))){
                    flag = true;
                    attrEle.setState(CheckType.DELETE);
                }
            }
            attrEle.setCiInfo(attrCiInfo);
            ele.getAttrUpdateInfo().add(attrEle);
        }
        if(flag && !ele.getState().equals(CheckType.DELETE)){
            ele.setState(CheckType.UPDATE);
        }
        return attrNames;
    }
}
